<?php

class CollectionService extends AbstractService
{
    /**
     * @var CollectionService
     */
    private static $instance;

    /**
     * Get instance of CollectionService.
     *
     * @return CollectionService
     */
    public static function get()
    {
        if (!self::$instance) {
            self::$instance = new CollectionService();
        }
        return self::$instance;
    }

    /**
     * Construction.
     */
    protected function __construct()
    {
        parent::__construct();
    }

    /**
     * 处理创建时的逻辑
     *
     * @param int $shardId
     * @return void
     */
    public function handleCreate($shardId)
    {

    }

    /**
     * 用户登录后触发逻辑
     *
     * @param int $userId
     * @return void
     */
    public function handleLogin($userId)
    {

    }

    /**
     * 提供当前 service 数据
     *
     * @param int $userId
     * @param boolean $visitMode default false
     * @return array(DebrisVoList, CollectionsVoList)
     */
    public function info($userId, $visitMode = false)
    {
        $debrisList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_DEBRIS);
        $collectionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_COLLECTIONS);
        $handbookList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_HANDBOOK);

        return array(
            $debrisList,
            $collectionList,
            $handbookList
        );
    }

    /**
     * 碎片处理（增，减）统一逻辑
     *
     * @param int $userId
     * @param int $itemDefId
     * @param int $count
     * @throws SzException
     * @return DebrisVoList
     */
    public function processDebris($userId, $itemDefId, $count)
    {
        $itemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $itemDefId, true);
        if ($itemSetting['category'] != GameConst::ENTITY_TYPE_DEBRIS) {
            throw new SzException(21202, $itemDefId);
        }

        /**
         * @var DebrisVoList $debrisList
         * @var DebrisVo $debris
         */
        $debrisList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_DEBRIS);
        $debris = $debrisList->getElement($itemDefId, false);

        // 无需操作
        if ($count == 0) {
            return $debrisList;
        }

        // 创建空对象
        if (!$debris) {
            $debris = new DebrisVo($userId, $itemDefId, 0, true);
        }

        // 如果是碎片扣除，判断碎片数量是否足够
        if ($count < 0 && $debris->getCount() < abs($count)) {
            throw new SzException(21201, $itemDefId);
        }

        $debris->setCount($debris->getCount() + $count);
        if ($_SERVER['is_payment_callback'] ?? false) $debris->persist();

        if ($debris->isInsert() && !SzUtility::checkArrayKey($itemDefId, $debrisList->getList())) {
            $debrisList->addElement($debris);
            $debrisList->setElement($itemDefId, $debris);
        } else {
            $debrisList->updateElement($itemDefId, $debris);
        }

        SzPersister::get()->setVoList($debrisList);

        return $debrisList;
    }

    /**
     * 获取当前杂项物品的数量
     *
     * @param $userId
     * @param $itemDefId
     * @return int
     */
    private function getDebrisCount($userId, $itemDefId)
    {
        /**
         * @var DebrisVoList $debrisList
         * @var DebrisVo $debris
         */
        $debrisList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_DEBRIS);
        $debris = $debrisList->getElement($itemDefId, false);
        return !$debris ? 0 : $debris->getCount();
    }

    /**
     * 物品是否已解锁可用
     *
     * @param int $userId
     * @param int $itemDefId
     * @throws SzException
     * @return boolean
     */
    public function isUnlock($userId, $itemDefId)
    {
        $itemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $itemDefId, true);
        if (UserService::get()->getUserLevel($userId) < $itemSetting['unlockLv']) {
            return false;
        }

        $collectionSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_ITEM_COLLECTION_SUIT, GameConst::COLLECTION_SUIT_ID, true);
        if (in_array($itemDefId, $collectionSetting['collection'])) {
            $specialCollectionType = json_decode(GameConst::COLLECTION_SPECIAL_TYPE, true);
            $itemType = substr($itemDefId, 0, 4);
            if (SzUtility::checkArrayKey($itemType, $specialCollectionType)) {
                $itemType = $specialCollectionType[$itemType];
                $itemDefId = $itemType . substr($itemDefId, -4);
                $itemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $itemDefId, true);
            }
            if (!empty($itemSetting['collectionRequest'])) {
                return $this->isCollectionUnlocked($userId, $itemDefId);
            }
        }
        return true;
    }

    /**
     * 验证是否收集品已解锁
     *
     * @param int $userId
     * @param int $targetItemDefId
     * @param CollectionsVoList $collectionList default null
     * @return boolean
     */
    private function isCollectionUnlocked($userId, $targetItemDefId, $collectionList = null)
    {
        if (!$collectionList) {
            $collectionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_COLLECTIONS);
        }
        /**@var CollectionsVo $collection */
        $collection = $collectionList->getElement($targetItemDefId, false);
        if (!$collection) {
            return false;
        }

        return ($collection->getIsDone() == GameConst::DEFAULT_BOOL_OF_TRUE) ? true : false;
    }

    /**
     * 兑换收集币
     *
     * @param int $userId
     * @param array $items
     * <pre>
     * array(
     *     itemDefId => count,
     *     ...
     * )
     * </pre>
     * @throws SzException
     * @return boolean
     */
    public function conversion($userId, $items)
    {
        $itemSettings = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, null, true);

        foreach ($items as $_itemDefId => $_count) {
            if (!SzUtility::checkArrayKey($_itemDefId, $itemSettings)) {
                continue;
            }
            $itemSetting = $itemSettings[$_itemDefId];

            if ($itemSetting['changeToCollectionCoin'] == -1) {
                throw new SzException(21206, $_itemDefId);
            }

            // 减碎片数量
            $count = $_count > 0 ? -$_count : $_count;
            GameService::get()->handleEntityViaBuild($userId, GameConst::ENTITY_ACTION_CONSUME, GameConst::ENTITY_TYPE_DEBRIS, $_itemDefId, $count, true);
            CommonFunctionService::get()->reportResourceUseDetails(GameConst::FUNC_COLLECTION_FUNC,$userId,0,GameConst::ENTITY_ACTION_CONSUME.GameConst::ENTITY_TYPE_DEBRIS.$_itemDefId.':'.$count.'|','conversion');//消耗资源上报

            // 加收集币
            $ratioCount = $count * $itemSetting['changeToCollectionCoin'];
            GameService::get()->handleEntity($userId, GameConst::ENTITY_ACTION_GET_COLLECTION_TOKEN, abs($ratioCount), true);
            CommonFunctionService::get()->reportResourceUseDetails(GameConst::FUNC_COLLECTION_FUNC,$userId,1,GameConst::ENTITY_ACTION_GET_COLLECTION_TOKEN.':'.abs($ratioCount).'|','conversion');//获得资源上报
        }

        return true;
    }

    /**
     * 提交物品
     *
     * @param int $userId 玩家ID
     * @param int $targetItemDefId 要合成的物品的itemDefId
     * @param int $consumeItemDefId 指定要提交的物品Id
     * @param int $consumeCount 用户指定物品数量
     * @throws SzException
     * @return CollectionsVo
     */
    public function submit($userId, $targetItemDefId, $consumeItemDefId, $consumeCount)
    {
        $targetItemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $targetItemDefId, true);
        $consumeItemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $consumeItemDefId, true);

        if (empty($targetItemSetting['collectionRequest'])) {
            throw new SzException(21207, $targetItemDefId);
        }

        $consumeEntityId = EntityService::get()->buildEntityId(GameConst::ENTITY_ACTION_CONSUME, $consumeItemSetting['moduleId'], $consumeItemDefId);
        if (!SzUtility::checkArrayKey($consumeEntityId, $targetItemSetting['collectionRequest'])) {
            throw new SzException(21213, $targetItemDefId);
        }

        if ($targetItemSetting['category'] == GameConst::ENTITY_TYPE_DEBRIS) {
            throw new SzException(21214, $targetItemDefId);
        }

        /**
         * @var CollectionsVoList $collectionList
         * @var CollectionsVo $collection
         */
        $collectionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_COLLECTIONS);
        $collection = $collectionList->getElement($targetItemDefId, false);

        if (!$collection) {
            $collectProgress = array();
            foreach ($targetItemSetting['collectionRequest'] as $_num) {
                $collectProgress[] = 0;
            }
            $collection = $this->createCollection($userId, $targetItemDefId, $collectProgress);
        }

        // 已完成的收集
        if ($collection->getIsDone()) {
            throw new SzException(21209, $targetItemDefId);
        }

        //$progress    [0:0,1:0]
        // 获取当前提交的是该收集项的第几个消耗物品
        $index = 0;
        foreach ($targetItemSetting['collectionRequest'] as $entityId => $num) {
            if ($entityId == $consumeEntityId) {
                break;
            }
            $index++;
        }
        $progress = $collection->getRawProgress();

        if (!isset($progress[$index])) {
            $progress[$index] = 0;
        }

        $consumeCount = abs($consumeCount);
        $progressCount = $progress[$index] + $consumeCount;

        // 判断是否超过上限，超过上限则以上限作为进度
        if ($progressCount > $targetItemSetting['collectionRequest'][$consumeEntityId]) {
            $consumeCount = $targetItemSetting['collectionRequest'][$consumeEntityId] - $progressCount;
            $progressCount = $targetItemSetting['collectionRequest'][$consumeEntityId];
        }

        // 扣除道具
        GameService::get()->handleEntity($userId, $consumeEntityId, $consumeCount, true, true);
        CommonFunctionService::get()->reportResourceUseDetails(GameConst::FUNC_COLLECTION_FUNC,$userId,0,$consumeEntityId.':'.$consumeCount.'|','submit');//消耗资源上报

        // 更新进度
        $progress[$index] = $progressCount;
        $collection->setProgress($progress);

        if ($collection->isInsert() && !SzUtility::checkArrayKey($targetItemDefId, $collectionList->getList())) {
            $collectionList->addElement($collection);
            $collectionList->setElement($targetItemDefId, $collection);
        } else {
            $collectionList->updateElement($targetItemDefId, $collection);
        }
        SzPersister::get()->setVoList($collectionList);

        // 数据采集:旅行屋解锁装饰物上交花
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:Travel|action:paid|',
                'item' => $targetItemDefId,
                'value' => 1
            )
        ));

        // 数据采集:旅行屋解锁装饰物上交花消耗
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_INPUT, array(
            'clientData' => array(
                'tid' => $consumeEntityId . ':' . $consumeCount,
                'item' => "Travel_paid"
            )
        ));

        return $collection;
    }

    /**
     * 完成收集并发奖（包括提交碎片）
     *
     * @param int $userId
     * @param int $targetItemDefId
     * @throws SzException
     * @return bool
     */
    public function finish($userId, $targetItemDefId)
    {
        $itemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $targetItemDefId, true);

        if (empty($itemSetting['collectionRequest'])) {
            throw new SzException(21207, $targetItemDefId);
        }

        /**
         * @var CollectionsVoList $collectionList
         * @var CollectionsVo $collection
         */
        $collectionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_COLLECTIONS);
        $collection = $collectionList->getElement($targetItemDefId, false);

        if (!$collection) {
            $collectProgress = array();
            foreach ($itemSetting['collectionRequest'] as $_entityId => $_num) {
                $collectProgress[] = 0;
            }
            $collection = $this->createCollection($userId, $targetItemDefId, $collectProgress);
        }

        if ($collection->getIsDone()) {
            throw new SzException(21212, $targetItemDefId);
        }

        // 进度验证
        $progress = $collection->getRawProgress();
        $index = 0; //消耗品下标
        foreach ($itemSetting['collectionRequest'] as $_consumeEntityId => $_count) {
            list(, , $subId) = EntityService::get()->parseEntityId($_consumeEntityId);
            $consumeItemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $subId, true);

            // 验证是否是非碎片物品，则判断物品是否已经全部提交
            if ($consumeItemSetting['category'] != GameConst::ENTITY_TYPE_DEBRIS && $progress[$index] < $_count) {
                throw new SzException(21215, $targetItemDefId);
            }

            // 条件完全满足，则跳过
            if ($progress[$index] >= $_count) {
                $index++;
                continue;
            }

            // 如果是碎片，则扣除碎片
            if ($consumeItemSetting['category'] == GameConst::ENTITY_TYPE_DEBRIS) {
                $debrisCount = $this->getDebrisCount($userId, $subId);

                // 判断碎片是否足够
                if ($debrisCount < $_count) {
                    throw new SzException(21208, $targetItemDefId);
                }

                $this->processDebris($userId, $subId, -$_count);
                $progress[$index] += $_count;
            }
            $index++;
        }


        // 进度更新
        $collection->setProgress($progress);
        $collection->setIsDone(1);

        if ($collection->isInsert() && !SzUtility::checkArrayKey($targetItemDefId, $collectionList->getList())) {
            $collectionList->addElement($collection);
            $collectionList->setElement($targetItemDefId, $collection);
        } else {
            $collectionList->updateElement($targetItemDefId, $collection);
        }

        SzPersister::get()->setVoList($collectionList);

        // 获得收集奖励
        $outputTid = '';
        foreach ($itemSetting['collectionReward'] as $_entityId => $_count) {
            GameService::get()->handleEntity($userId, $_entityId, $_count, true, true);
            $outputTid .= $_entityId . ':' . $_count . '|';
        }

        // 数据采集:旅行屋鲜花、装饰物、日本旅行屋内部物品解锁后领取奖励
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:Travel|action:receive|',
                'item' => $targetItemDefId,
                'value' => 1
            )
        ));

        // 数据采集:旅行屋鲜花、装饰物、日本旅行屋内部物品解锁后领取奖励产出
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_OUTPUT, array(
            'clientData' => array(
                'tid' => $outputTid,
                'item' => 'Travel_receive_' . $targetItemDefId
            )
        ));

        return true;
    }

    /**
     * 完成系列收集
     *
     * @param int $userId
     * @param int $suitId
     * @throws SzException
     * @return boolean
     */
    public function finishSeries($userId, $suitId)
    {
        $collectionSuitSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_ITEM_COLLECTION_SUIT, $suitId, true);

        // 验证成员收集品是否解锁
        $collectionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_COLLECTIONS);
        foreach ($collectionSuitSetting['collection'] as $_collectionId) {
            if (!$this->isCollectionUnlocked($userId, $_collectionId, $collectionList)) {
                throw new SzException(21210, $suitId);
            }
        }

        // 验证系列是否已经完成
        $collectionSuitIds = UserService::get()->getInfo($userId, GameConst::USER_INFO_TYPE_COLLECTED_SERIES);
        if (in_array($suitId, $collectionSuitIds)) {
            throw new SzException(21211, $suitId);
        }

        // 数据更新
        $collectionSuitIds[] = $suitId;
        UserService::get()->updateProfileInfo($userId, GameConst::USER_INFO_TYPE_COLLECTED_SERIES, $collectionSuitIds);

        // 获得奖励
        $outputTid = '';
        foreach ($collectionSuitSetting['reward'] as $_entityId => $_count) {
            GameService::get()->handleEntity($userId, $_entityId, $_count, true, true);
            $outputTid .= $_entityId . ':' . $_count . '|';
        }

        // 数据采集:旅行屋所有鲜花、装饰物、日本旅行屋内部物品解锁后领取终极奖励
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:Travel|action:complete_all|',
                'item' => '',
                'value' => 1
            )
        ));

        // 数据采集:旅行屋所有鲜花、装饰物、日本旅行屋内部物品解锁后领取终极奖励产出
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_OUTPUT, array(
            'clientData' => array(
                'tid' => $outputTid,
                'item' => 'Travel_receive_all'
            )
        ));

        return true;
    }

    /**
     * 创建收集品数据
     *
     * @param $userId
     * @param $itemId
     * @param $collectionProgress
     * @return CollectionsVo
     */
    private function createCollection($userId, $itemId, $collectionProgress)
    {
        $collectionVo = new CollectionsVo
        (
            $userId,
            $itemId,
            json_encode($collectionProgress),
            0,
            true
        );
        return $collectionVo;
    }

    /**
     * 提交图鉴内容
     *
     * @param int $userId
     * @param int $collectionId
     * @param int $collectCount
     * @param int $reqTime
     * @throws SzException
     * @return HandbookVo
     */
    public function submitHandbook($userId, $collectionId, $collectCount, $reqTime)
    {
        $itemSetting = SzConfig::get()->loadAppConfig(ModuleItemConst::CONFIG_ITEM_DEFS, $collectionId, true);

        if (!$itemSetting['isHandbook']) {
            throw new SzException(21207, $collectionId);
        }

        /**
         * @var HandbookVoList $handbookList
         * @var HandbookVo $handbook
         */
        $handbookList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_HANDBOOK);
        $handbook = $handbookList->getElement($collectionId, false);

        $consumeEntityId = EntityService::get()->buildEntityId(GameConst::ENTITY_ACTION_CONSUME, $itemSetting['moduleId'], $collectionId);
        if (!$handbook) {
            $handbook = new HandbookVo(
                $userId, // userId
                $collectionId, // collectionId
                json_encode([0]), // progress
                0, // step
                true
            );
        }
        if ($collectCount <= 0) {
            return $handbook;
        }

        // 更新进度
        $progress = $handbook->getRawProgress();
        $progress[0] += abs($collectCount);

        $handbookSubmit = $itemSetting['handbookSubmit'];
        $handbookSubmitLimit = array_pop($handbookSubmit);

        // 判断是否属于超上限的提交
        if ($progress[0] > $handbookSubmitLimit) {
            throw new SzException(21216, $handbook);
        }

        // 扣除消耗
        GameService::get()->handleEntity($userId, $consumeEntityId, $collectCount, true);

        // 计算阶段
        $nowStage = $this->sendStageReward($userId, $collectionId, $progress[0], $itemSetting['handbookSubmit'], $handbook->getStep(), $itemSetting['handbookReward']);

        // 更新图鉴
        $handbook->setProgress($progress);
        $handbook->setStep($nowStage);

        if ($handbook->isInsert() && !SzUtility::checkArrayKey($collectionId, $handbookList->getList())) {
            $handbookList->addElement($handbook);
            $handbookList->setElement($collectionId, $handbook);
        } else {
            $handbookList->updateElement($collectionId, $handbook);
        }
        SzPersister::get()->setVoList($handbookList);


        // 数据采集: 图鉴上交花
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:Book|action:paid|',
                'item' => $collectionId,
                'value' => 1
            )
        ));

        // 数据采集: 图鉴上交花消耗物品
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_INPUT, array(
            'clientData' => array(
                'tid' => $consumeEntityId . ':' . $collectCount . '|',
                'item' => 'Book_paid',
                'value' => 1
            )
        ));
        return $handbook;
    }

    /**
     * 发送阶段奖励
     *
     * @param $userId int 玩家ID
     * @param $collectionId int 道具id
     * @param $nowCount int 当前进度
     * @param $needCount array 需求进度数组
     * @param $nowStage int 当前阶段
     * @param $rewardConfig array 奖励配置
     * @return int;
     */
    private function sendStageReward($userId, $collectionId, $nowCount, $needCount, $nowStage, $rewardConfig)
    {
        foreach ($needCount as $tmpStage => $tmpCount) {
            if ($nowCount < $tmpCount || $nowStage >= $tmpStage + 1) {
                continue;
            }

            // 首次达标发奖
            $nowStage = $tmpStage + 1;
            $tid = '';
            foreach ($rewardConfig[$tmpStage] as $entityId => $count) {
                GameService::get()->handleEntity($userId, $entityId, $count, true, true);
                $tid .= $entityId . ':' . $count . '|';
            }

            // 数据采集: 图鉴完成任务领取奖励
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_UPGRADE, array(
                'clientData' => array(
                    'tid' => 'Module:Book|action:receive|',
                    'item' => $collectionId,
                    'value' => 1
                )
            ));

            if ($tid) {
                // 数据采集: 图鉴完成任务领取奖励产出
                StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_OUTPUT, array(
                    'clientData' => array(
                        'tid' => $tid,
                        'item' => 'Book_receive_' . $collectionId,
                        'value' => 1
                    )
                ));
            }
        }

        return $nowStage;
    }
}
